/*******************************************************************************
 *                                    ZLG
 *                         ----------------------------
 *                         innovating embedded platform
 *
 * Copyright (c) 2001-present Guangzhou ZHIYUAN Electronics Co., Ltd.
 * All rights reserved.
 *
 * Contact information:
 * web site:    https://www.zlg.cn
 *******************************************************************************/
/*******************************************************************************
 * Includes
 ******************************************************************************/
#include "driver/efm/hc32f4a0_efm.h"
#include "core/include/hc32f4a0_regs_base.h"
#include "core/include/hc32f4a0_regs_gpio.h"
#include "core/include/hc32f4a0_regs_efm.h"
#include "core/include/hc32f4a0_clk.h"
#include "common/hc32f4a0_common.h"
#include "common/hc32f4a0_errno.h"
#include <string.h>
#include <stdlib.h>

/*******************************************************************************
 * Static
 ******************************************************************************/
/* \brief EFM 擦除超时时间*/
static uint32_t __g_efm_erase_timeout   = 0;
/* \brief EFM 编程超时时间 */
static uint32_t __g_efm_pgm_timeout     = 0;
/* \brief EFM 连续编程超时时间 */
static uint32_t __g_efm_seq_pgm_timeout = 0;
/* \brief EFM 寄存器值备份*/
static uint32_t __g_efm_frmc_bak        = 0;
static uint32_t __g_efm_fwmc_bak        = 0;
static uint32_t __g_efm_fstp_bak        = 0;


/*******************************************************************************
 * Code
 ******************************************************************************/
/**
 * \brief EFM 擦写模式寄存器写使能
 */
__attribute__((section(".ramfunc"))) void efm_fwmc_unlock(void){
    if (!IS_VALID_EFM_UNLOCK()) {
        return;
    }

    WRITE_REG32(HC32F4A0_EFM->KEY1, 0x01234567UL);
    WRITE_REG32(HC32F4A0_EFM->KEY1, 0xFEDCBA98UL);
}

/**
 * \brief EFM 擦写模式寄存器写保护
 */
__attribute__((section(".ramfunc"))) void efm_fwmc_lock(void){
    if (!IS_VALID_EFM_UNLOCK()) {
        return;
    }

    SET_REG32_BIT(HC32F4A0_EFM->FWMC, EFM_FWMC_KEY1LOCK);
}

/**
 * \brief EFM 读等待周期设置
 *
 * \param[in] wait_cycle 设置的等待周期
 *
 * \retval 成功返回0
 */
__attribute__((section(".ramfunc"))) int efm_wait_cycle_set(uint32_t wait_cycle){

    if ((!IS_VALID_EFM_UNLOCK()) || (!IS_VALID_EFM_WAIT_CYCLE(wait_cycle))) {
        return -EINVAL;
    }

    MODIFY_REG32(HC32F4A0_EFM->FRMC, EFM_FRMC_FLWT, wait_cycle);
    return 0;
}

/**
 * \brief EFM 状态标志
 */
__attribute__((section(".ramfunc"))) static int __flag_sta_get(uint32_t flag){
    return ((0 == READ_REG32_BIT(HC32F4A0_EFM->FSR, flag)) ? 0 : 1);
}

/**
 * \brief 根据地址获取扇区编号
 *
 * \param[in] addr 扇区起始地址
 *
 * \retval 成功返回扇区编号
 */
__attribute__((section(".ramfunc"))) int efm_sec_num_get(uint32_t addr){
    uint32_t addr_start;

    addr_start = (addr & ~(0xFFF));

    return (addr_start / SECTOR_SIZE);
}

/**
 * \brief 获取 EFM
 */
__attribute__((section(".ramfunc"))) void efm_unique_id_get(uint32_t *pUID, uint32_t *pLength){
    //todo
}

/**
 * \brief EFM 单扇区写使能
 *
 * \param[in] sec_num 扇区编号
 *
 * \retval 成功返回 0
 */
__attribute__((section(".ramfunc"))) void efm_sec_single_unlock(uint8_t sec_num){
    volatile uint32_t *p_efm_FxNWPRTy;
    const uint8_t      reg_idx = sec_num / REG_LENGTH;
    const uint8_t      bit_pos = sec_num % REG_LENGTH;
    if (!IS_VALID_EFM_UNLOCK()) {
        return;
    }

    p_efm_FxNWPRTy = (volatile uint32_t*)((uint32_t)(&HC32F4A0_EFM->F0NWPRT0) +
                                        ((uint32_t)reg_idx << 2));
    MODIFY_REG32(*p_efm_FxNWPRTy, 1 << bit_pos, (uint32_t)(1 << bit_pos));
}

/**
 * \brief EFM 单扇区写保护
 *
 * \param[in] sec_num 扇区编号
 *
 * \retval 成功返回 0
 */
__attribute__((section(".ramfunc"))) void efm_sec_single_lock(uint8_t sec_num){
    volatile uint32_t *p_efm_FxNWPRTy;
    const uint8_t      reg_idx = sec_num / REG_LENGTH;
    const uint8_t      bit_pos = sec_num % REG_LENGTH;
    if (!IS_VALID_EFM_UNLOCK()) {
        return;
    }

    p_efm_FxNWPRTy = (volatile uint32_t*)((uint32_t)(&HC32F4A0_EFM->F0NWPRT0) +
                                        ((uint32_t)reg_idx << 2));
    MODIFY_REG32(*p_efm_FxNWPRTy, 1 << bit_pos, (uint32_t)(0 << bit_pos));
}

/**
 * \brief EFM 连续扇区写使能
 *
 * \param[in] addr_start 扇区起始地址
 * \param[in] sec_cnt    写使能的扇区数量
 *
 * \retval 成功返回 0
 */
__attribute__((section(".ramfunc"))) int efm_sec_sequential_unlock(uint32_t addr_start, uint16_t sec_cnt){
    int                ret = -EINVAL;
    volatile uint32_t *pEFM_FxNWPRTy;
    uint32_t           start_sect;
    uint32_t           end_sect;
    uint32_t           addr_end;
    uint16_t           num;
    uint16_t           need_sector;
    uint16_t           start_reg_idx;
    uint16_t           start_bit_pos;
    uint16_t           end_reg_idx;
    uint16_t           end_bit_pos;

    addr_end = addr_start + sec_cnt * SECTOR_SIZE - 1;

    if ((!IS_VALID_EFM_UNLOCK()) || (!IS_VALID_EFM_SECTOR_NUM(sec_cnt))) {
        return -EINVAL;
    }

    if (addr_end < EFM_END_ADDR + SECTOR_SIZE) {
        start_sect    = (uint16_t)(addr_start / SECTOR_SIZE);      /* 计算起始扇区 */
        need_sector   = sec_cnt;                                   /* 计算需求扇区*/
        end_sect      = (uint16_t)(start_sect + need_sector - 1U); /* 计算结束扇区*/
        start_reg_idx = (uint16_t)(start_sect / REG_LENGTH);       /* 起始扇区寄存器偏移 */
        start_bit_pos = (uint16_t)(start_sect % REG_LENGTH);       /* 起始扇区位偏移 */
        end_reg_idx   = (uint16_t)(end_sect / REG_LENGTH);         /* 结束扇区寄存器偏移 */
        end_bit_pos   = (uint16_t)(end_sect % REG_LENGTH);         /* 结束扇区位偏移*/

        pEFM_FxNWPRTy = (volatile uint32_t*)((uint32_t)(&HC32F4A0_EFM->F0NWPRT0) +
                ((uint32_t)start_reg_idx << 2UL));

        if (start_reg_idx == end_reg_idx) {
            for(num = start_bit_pos; num <= end_bit_pos; num++) {
                MODIFY_REG32(*pEFM_FxNWPRTy, (uint32_t)1UL << num, (uint32_t)1 << num);
            }
        } else {
            for(num = start_bit_pos; num < REG_LENGTH; num++) {
                MODIFY_REG32(*pEFM_FxNWPRTy, (uint32_t)1UL << num, (uint32_t)1 << num);
            }
            start_reg_idx += 1U;
            pEFM_FxNWPRTy = (volatile uint32_t*)((uint32_t)(&HC32F4A0_EFM->F0NWPRT0) +
                    ((uint32_t)end_reg_idx << 2U));
            for (num = 0U; num <= end_bit_pos; num++) {
                MODIFY_REG32(*pEFM_FxNWPRTy, (uint32_t)1UL << num, (uint32_t)1 << num);
            }

            while (start_reg_idx < end_reg_idx) {
                pEFM_FxNWPRTy = (volatile uint32_t*)((uint32_t)(&HC32F4A0_EFM->F0NWPRT0) +
                        ((uint32_t)start_reg_idx << 2U));
                WRITE_REG32(*pEFM_FxNWPRTy, 0xFFFFFFFFUL);
                start_reg_idx += 1U;
            }
            ret = 0;
        }
    }
    return ret;
}

/**
 * \brief EFM 连续扇区写保护
 *
 * \param[in] addr_start 扇区起始地址
 * \param[in] sec_cnt    写保护的扇区数量
 *
 * \retval 成功返回 0
 */
__attribute__((section(".ramfunc"))) int efm_sec_sequential_lock(uint32_t addr_start, uint16_t sec_cnt){
    int                ret = -EINVAL;
    volatile uint32_t *pEFM_FxNWPRTy;
    uint32_t           start_sect;
    uint32_t           end_sect;
    uint32_t           addr_end;
    uint16_t           num;
    uint16_t           need_sector;
    uint16_t           start_reg_idx;
    uint16_t           start_bit_pos;
    uint16_t           end_reg_idx;
    uint16_t           end_bit_pos;

    addr_end = addr_start + sec_cnt * SECTOR_SIZE - 1;

    if ((!IS_VALID_EFM_UNLOCK()) || (!IS_VALID_EFM_SECTOR_NUM(sec_cnt))) {
        return -EINVAL;
    }

    if (addr_end < EFM_END_ADDR + SECTOR_SIZE) {
        start_sect    = (uint16_t)(addr_start / SECTOR_SIZE);       /* 计算起始扇区 */
        need_sector   = sec_cnt;                                    /* 计算需求扇区*/
        end_sect      = (uint16_t)(start_sect + need_sector - 1U);  /* 计算结束扇区*/
        start_reg_idx = (uint16_t)(start_sect / REG_LENGTH);        /* 起始扇区寄存器偏移 */
        start_bit_pos = (uint16_t)(start_sect % REG_LENGTH);        /* 起始扇区位偏移 */
        end_reg_idx   = (uint16_t)(end_sect / REG_LENGTH);          /* 结束扇区寄存器偏移 */
        end_bit_pos   = (uint16_t)(end_sect % REG_LENGTH);          /* 结束扇区位偏移*/

        pEFM_FxNWPRTy = (volatile uint32_t*)((uint32_t)(&HC32F4A0_EFM->F0NWPRT0) +
                ((uint32_t)start_reg_idx << 2UL));

        if (start_reg_idx == end_reg_idx) {
            for(num = start_bit_pos; num <= end_bit_pos; num++) {
                MODIFY_REG32(*pEFM_FxNWPRTy, (uint32_t)1UL << num, (uint32_t)0 << num);
            }
        } else {
            for(num = start_bit_pos; num < REG_LENGTH; num++) {
                MODIFY_REG32(*pEFM_FxNWPRTy, (uint32_t)1UL << num, (uint32_t)0 << num);
            }
            start_reg_idx += 1U;
            pEFM_FxNWPRTy = (volatile uint32_t*)((uint32_t)(&HC32F4A0_EFM->F0NWPRT0) +
                    ((uint32_t)end_reg_idx << 2U));
            for (num = 0U; num <= end_bit_pos; num++) {
                MODIFY_REG32(*pEFM_FxNWPRTy, (uint32_t)1UL << num, (uint32_t)0 << num);
            }

            while (start_reg_idx < end_reg_idx) {
                pEFM_FxNWPRTy = (volatile uint32_t*)((uint32_t)(&HC32F4A0_EFM->F0NWPRT0) +
                        ((uint32_t)start_reg_idx << 2U));
                WRITE_REG32(*pEFM_FxNWPRTy, 0x0UL);
                start_reg_idx += 1U;
            }
            ret = 0;
        }
    }
    return ret;
}

/**
 * \brief EFM 扇区擦除
 *
 * \param[in] addr 要擦除的扇区的起始地址
 *
 * \retval 成功返回 0
 */
__attribute__((section(".ramfunc"))) int efm_sec_erase(uint32_t addr){
    int      ret          = 0;
    uint32_t timeout      = 0;
    uint32_t tmp;
    uint32_t efm_flag     = EFM_FLAG_RDY1;
    uint32_t efm_clr_flag = EFM_FLAG_CLR_OPTEND1;

    if ((!IS_VALID_EFM_ADDR(addr)) || (!IS_VALID_EFM_UNLOCK())) {
        return -EINVAL;
    }

    /* 清除错误标志*/
    SET_REG32_BIT(HC32F4A0_EFM->FSCLR, EFM_CLR_FLAG_MASK);
    /* 获取缓存状态*/
    tmp = READ_REG32_BIT(HC32F4A0_EFM->FRMC, EFM_CACHE_MASK);
    /* 关闭缓存*/
    CLEAR_REG32_BIT(HC32F4A0_EFM->FRMC, EFM_CACHE_MASK);
    /* 设置扇区擦除模式 */
    MODIFY_REG32(HC32F4A0_EFM->FWMC, EFM_FWMC_PEMOD, EFM_MODE_ERASESECTOR);
    /* 擦除 */
    RW_MEM32(addr & 0xFFFFFFFCUL) = (uint32_t)0;
    if((addr < EFM_ADDR_SECTOR128) || (addr >= EFM_OTP_BLOCK16)) {
        efm_flag     = EFM_FLAG_RDY0;
        efm_clr_flag = EFM_FLAG_CLR_OPTEND0;
    }
    while(1 != __flag_sta_get(efm_flag)) {
        timeout ++;
        if (timeout >= __g_efm_erase_timeout) {
            ret = -ETIME;
            break;
        }
    }
    /* 清除操作结束标志*/
    SET_REG32_BIT(HC32F4A0_EFM->FSCLR, efm_clr_flag);
    /* 设置只读模式*/
    MODIFY_REG32(HC32F4A0_EFM->FWMC, EFM_FWMC_PEMOD, EFM_MODE_READONLY);
    /* 恢复缓存*/
    MODIFY_REG32(HC32F4A0_EFM->FRMC, EFM_CACHE_MASK, tmp);

    return ret;
}

/**
 * \brief EFM 芯片擦除
 *
 * \param[in] erase_mode 擦除模式
 * \param[in] addr       擦除起始地址
 *
 * \retval 成功返回 0
 */
__attribute__((section(".ramfunc"))) int efm_chip_erase(uint32_t erase_mode, uint32_t addr){
    int      ret      = 0;
    uint32_t time_out = 0;
    uint32_t tmp;
    uint32_t efm_flag;
    uint32_t efm_clr_flag;

    if ((!IS_VALID_EFM_CHIP_ERASE_ADDR(addr)) ||
            (!IS_VALID_EFM_UNLOCK()) ||
            (!IS_VALID_EFM_ERASE_MODE(erase_mode))) {
        return -EINVAL;
    }

    /* 清除错误标志*/
    SET_REG32_BIT(HC32F4A0_EFM->FSCLR, EFM_CLR_FLAG_MASK);
    /* 获取缓存状态*/
    tmp = READ_REG32_BIT(HC32F4A0_EFM->FRMC, EFM_CACHE_MASK);
    /* 关闭缓存*/
    CLEAR_REG32_BIT(HC32F4A0_EFM->FRMC, EFM_CACHE_MASK);
    /* 如果使能了交换，关闭它 */
    if (RW_MEM32(EFM_SWAP_ADDR) == EFM_SWAP_DATA) {
        /* 设置扇区擦除模式 */
        MODIFY_REG32(HC32F4A0_EFM->FWMC, EFM_FWMC_PEMOD, EFM_MODE_ERASESECTOR);
        /* 关闭 FLASH 切换功能 */
        RW_MEM32(EFM_SWAP_ADDR) = 0x0UL;
        while (1 != __flag_sta_get(EFM_FLAG_OPTEND0)) {
            time_out++;
            if (time_out >= __g_efm_erase_timeout) {
                ret = -ETIME;
                break;
            }
        }
        /* 清除操作完成标志*/
        SET_REG32_BIT(HC32F4A0_EFM->FSCLR, EFM_FLAG_CLR_OPTEND0);
    }
    /* 设置芯片擦除模式 */
    MODIFY_REG32(HC32F4A0_EFM->FWMC, EFM_FWMC_PEMOD, erase_mode);
    if (erase_mode == EFM_MODE_ERASECHIP1) {
        if (addr >= EFM_ADDR_SECTOR128) {
            efm_flag     = EFM_FLAG_RDY1;
            efm_clr_flag = EFM_FLAG_CLR_OPTEND1;
            /* Flash1 关闭写保护*/
            WRITE_REG32(HC32F4A0_EFM->F1NWPRT0, 0xFFFFFFFFUL);
            WRITE_REG32(HC32F4A0_EFM->F1NWPRT1, 0xFFFFFFFFUL);
            WRITE_REG32(HC32F4A0_EFM->F1NWPRT2, 0xFFFFFFFFUL);
            WRITE_REG32(HC32F4A0_EFM->F1NWPRT3, 0xFFFFFFFFUL);
        } else {
            efm_flag     = EFM_FLAG_RDY0;
            efm_clr_flag = EFM_FLAG_CLR_OPTEND0;
            /* Flash0 关闭写保护 */
            WRITE_REG32(HC32F4A0_EFM->F0NWPRT0, 0xFFFFFFFFUL);
            WRITE_REG32(HC32F4A0_EFM->F0NWPRT1, 0xFFFFFFFFUL);
            WRITE_REG32(HC32F4A0_EFM->F0NWPRT2, 0xFFFFFFFFUL);
            WRITE_REG32(HC32F4A0_EFM->F0NWPRT3, 0xFFFFFFFFUL);
        }

        /* 擦除 */
        RW_MEM32(addr & 0xFFFFFFFCUL) = (uint32_t)0UL;
        while (1 != __flag_sta_get(efm_flag)) {
            time_out++;
            if (time_out >= __g_efm_erase_timeout) {
                ret = -ETIME;
                break;
            }
        }
        /* 清除操作完成标志*/
        SET_REG32_BIT(HC32F4A0_EFM->FSCLR, efm_clr_flag);
    } else {
        /* Flash0 和 Flash1 关闭写保护  */
        WRITE_REG32(HC32F4A0_EFM->F0NWPRT0, 0xFFFFFFFFUL);
        WRITE_REG32(HC32F4A0_EFM->F0NWPRT1, 0xFFFFFFFFUL);
        WRITE_REG32(HC32F4A0_EFM->F0NWPRT2, 0xFFFFFFFFUL);
        WRITE_REG32(HC32F4A0_EFM->F0NWPRT3, 0xFFFFFFFFUL);
        WRITE_REG32(HC32F4A0_EFM->F1NWPRT0, 0xFFFFFFFFUL);
        WRITE_REG32(HC32F4A0_EFM->F1NWPRT1, 0xFFFFFFFFUL);
        WRITE_REG32(HC32F4A0_EFM->F1NWPRT2, 0xFFFFFFFFUL);
        WRITE_REG32(HC32F4A0_EFM->F1NWPRT3, 0xFFFFFFFFUL);
        /* 擦除 */
        RW_MEM32(addr & 0xFFFFFFFCUL) = (uint32_t)0UL;
        while (1 != __flag_sta_get(EFM_FLAG_RDY0)) {
            time_out++;
            if (time_out >= __g_efm_erase_timeout) {
                ret = -ETIME;
                break;
            }
        }
        /* 清除操作完成标志*/
        SET_REG32_BIT(HC32F4A0_EFM->FSCLR, EFM_FLAG_CLR_OPTEND0);
        SET_REG32_BIT(HC32F4A0_EFM->FSCLR, EFM_FLAG_CLR_OPTEND1);
    }
    /* 设置为只读模式 */
    MODIFY_REG32(HC32F4A0_EFM->FWMC, EFM_FWMC_PEMOD, EFM_MODE_READONLY);
    /* 恢复缓存 */
    MODIFY_REG32(HC32F4A0_EFM->FRMC, EFM_CACHE_MASK, tmp);
    return ret;
}

/**
 * \brief EFM 单次编程
 *
 * \param[in] addr  编程地址
 * \param[in] data  编程数据
 *
 * \retval 成功返回 0
 */
__attribute__((section(".ramfunc"))) int efm_single_program(uint32_t addr, uint32_t data){
    int      ret          = 0;
    uint32_t timeout      = 0;
    uint32_t tmp;
    uint32_t efm_flag     = EFM_FLAG_RDY1;
    uint32_t efm_clr_flag = EFM_FLAG_CLR_OPTEND1;

    if ((!IS_VALID_EFM_ADDR(addr)) ||
            (!IS_VALID_EFM_UNLOCK()) ||
            (!IS_VALID_EFM_ADDR_ALIGNMENT(addr))) {
        return -EINVAL;
    }

    /* 清除错误标志*/
    SET_REG32_BIT(HC32F4A0_EFM->FSCLR, EFM_CLR_FLAG_MASK);
    /* 获取缓存状态*/
    tmp = READ_REG32_BIT(HC32F4A0_EFM->FRMC, EFM_CACHE_MASK);
    /* 关闭缓存*/
    CLEAR_REG32_BIT(HC32F4A0_EFM->FRMC, EFM_CACHE_MASK);
    /* 设置单编程模式*/
    MODIFY_REG32(HC32F4A0_EFM->FWMC, EFM_FWMC_PEMOD, EFM_MODE_PROGRAMSINGLE);
    /* 写入数据 */
    RW_MEM32(addr) = (uint32_t)data;
    if ((addr < EFM_ADDR_SECTOR128) || (addr >= EFM_OTP_BLOCK16)) {
        efm_flag     = EFM_FLAG_RDY0;
        efm_clr_flag = EFM_FLAG_CLR_OPTEND0;
    }
    /* 等待操作结束 */
    while (1 != __flag_sta_get(efm_flag)) {
        timeout ++;
        if (timeout >= __g_efm_pgm_timeout) {
            ret = -ETIME;
            break;
        }
    }
    if (data != RW_MEM32(addr)) {
        ret = -EDATA;
    }
    /* 清除操作结束标志 */
    SET_REG32_BIT(HC32F4A0_EFM->FSCLR, efm_clr_flag);
    /* 设置为只读模式 */
    MODIFY_REG32(HC32F4A0_EFM->FWMC, EFM_FWMC_PEMOD, EFM_MODE_READONLY);
    /* 恢复缓存功能*/
    MODIFY_REG32(HC32F4A0_EFM->FRMC, EFM_CACHE_MASK, tmp);

    return ret;
}

/**
 * \brief EFM 编程回读
 *
 * \param[in] addr  编程地址
 * \param[in] data  编程数据
 *
 * \retval 成功返回 0
 */
__attribute__((section(".ramfunc"))) int efm_program_readback(uint32_t addr, uint32_t data){
    int      ret           = 0;
    uint32_t time_out      = 0;
    uint32_t tmp;
    uint32_t efm_flag      = EFM_FLAG_RDY1;
    uint32_t efm_clr_flag  = EFM_FLAG_CLR_OPTEND1;
    uint32_t efm_flag1     = EFM_FLAG_MISMTCH1;
    uint32_t efm_clr_flag1 = EFM_FLAG_CLR_MISMTCH1;

    if ((!IS_VALID_EFM_ADDR(addr)) ||
            (!IS_VALID_EFM_UNLOCK()) ||
            (!IS_VALID_EFM_ADDR_ALIGNMENT(addr))) {
        return -EINVAL;
    }

    /* 清除错误标志*/
    SET_REG32_BIT(HC32F4A0_EFM->FSCLR, EFM_CLR_FLAG_MASK);
    /* 获取缓存状态*/
    tmp = READ_REG32_BIT(HC32F4A0_EFM->FRMC, EFM_CACHE_MASK);
    /* 关闭缓存*/
    CLEAR_REG32_BIT(HC32F4A0_EFM->FRMC, EFM_CACHE_MASK);
    /* 设置编程和回读模式*/
    MODIFY_REG32(HC32F4A0_EFM->FWMC, EFM_FWMC_PEMOD, EFM_MODE_PROGRAMREADBACK);
    /* 写入数据*/
    RW_MEM32(addr) = (uint32_t)data;
    if ((addr < EFM_ADDR_SECTOR128) || (addr >= EFM_OTP_BLOCK16)) {
        efm_flag      = EFM_FLAG_RDY0;
        efm_clr_flag  = EFM_FLAG_CLR_OPTEND0;
        efm_flag1     = EFM_FLAG_MISMTCH0;
        efm_clr_flag1 = EFM_FLAG_CLR_MISMTCH0;
    }
    while (1 != __flag_sta_get(efm_flag)) {
        time_out++;
        if (time_out >= __g_efm_pgm_timeout) {
            ret = -ETIME;
            break;
        }
    }
    /* 获取标志 MISMTCH0 */
    if (1 == __flag_sta_get(efm_flag1)) {
        /* 清除 PGMISMTCH */
        SET_REG32_BIT(HC32F4A0_EFM->FSCLR, efm_clr_flag1);
        ret = -EPERM;
    }
    /* 清除操作结束标志 */
    SET_REG32_BIT(HC32F4A0_EFM->FSCLR, efm_clr_flag);
    /* 设置为只读模式 */
    MODIFY_REG32(HC32F4A0_EFM->FWMC, EFM_FWMC_PEMOD, EFM_MODE_READONLY);
    /* 恢复缓存功能 */
    MODIFY_REG32(HC32F4A0_EFM->FRMC, EFM_CACHE_MASK, tmp);

    return ret;
}

/**
 * \brief EFM 连续编程
 *
 * \param[in] addr  编程起始地址
 * \param[in] len   编程长度
 * \param[in] p_buf 要写入的缓存
 *
 * \retval 成功返回 0
 */
__attribute__((section(".ramfunc"))) int efm_sequence_program(uint32_t        addr,
                                                              uint32_t        len,
                                                              const uint32_t *p_buf){
    int      ret           = 0;
    uint32_t time_out;
    uint32_t tmp;
    uint32_t loop_words    = len >> 2;
    uint32_t efm_flag      = EFM_FLAG_OPTEND0;
    uint32_t efm_flag1     = EFM_FLAG_RDY0;
    uint32_t efm_clr_flag  = EFM_FLAG_CLR_OPTEND0;
    uint32_t src_data_addr = (uint32_t)p_buf;

    if ((!IS_VALID_EFM_ADDR(addr)) ||
            (!IS_VALID_EFM_UNLOCK()) ||
            (!IS_VALID_EFM_ADDR_ALIGNMENT(addr))) {
        return -EINVAL;
    }

    /* 清除错误标志*/
    SET_REG32_BIT(HC32F4A0_EFM->FSCLR, EFM_CLR_FLAG_MASK);
    /* 获取缓存状态 */
    tmp = READ_REG32_BIT(HC32F4A0_EFM->FRMC, EFM_CACHE_MASK);
    /* 关闭缓存*/
    CLEAR_REG32_BIT(HC32F4A0_EFM->FRMC, EFM_CACHE_MASK);
    /* 设置为连续编程模式 */
    MODIFY_REG32(HC32F4A0_EFM->FWMC, EFM_FWMC_PEMOD, EFM_MODE_PROGRAMSEQUENCE);
    /* 写入数据 */
    while ((loop_words--) > 0) {
        RW_MEM32(addr) = RW_MEM32(src_data_addr);
        /* 等待操作结束 */
        if ((addr >= EFM_ADDR_SECTOR128) && (addr < EFM_END_ADDR)) {
            efm_flag     = EFM_FLAG_OPTEND1;
            efm_flag1    = EFM_FLAG_RDY1;
            efm_clr_flag = EFM_FLAG_CLR_OPTEND1;
        }
        time_out = 0;
        while (1 != __flag_sta_get(efm_flag)) {
            if (time_out++ >= __g_efm_pgm_timeout) {
                ret = -ETIME;
                break;
            }
        }
        time_out = 0;
        /* 清除操作结束标志 */
        while (0 != __flag_sta_get(efm_flag)) {
            if (time_out++ >= __g_efm_seq_pgm_timeout) {
                ret = -ETIME;
                break;
            }
            /* 清除结束标志 */
            SET_REG32_BIT(HC32F4A0_EFM->FSCLR, efm_clr_flag);
        }
        if (ret != 0) {
            break;
        }
        addr += 4;
        src_data_addr += 4;
        if (addr == EFM_ADDR_SECTOR128) {
            /* 设置为只读模式 */
            MODIFY_REG32(HC32F4A0_EFM->FWMC, EFM_FWMC_PEMOD, EFM_MODE_READONLY);
            time_out = 0;
            /* 等待 FLASH 准备好*/
            while (1 != __flag_sta_get(efm_flag1)) {
                time_out++;
                if (time_out >= __g_efm_pgm_timeout) {
                    ret = -ETIME;
                    break;
                }
            }
            if (ret == 0) {
                /* 设置连续编程模式 */
                MODIFY_REG32(HC32F4A0_EFM->FWMC, EFM_FWMC_PEMOD, EFM_MODE_PROGRAMSEQUENCE);
            }
        }
    }
    /* 设置为只读模式 */
    MODIFY_REG32(HC32F4A0_EFM->FWMC, EFM_FWMC_PEMOD, EFM_MODE_READONLY);
    time_out = 0;
    /* 等待 FLASH 准备好*/
    while (1 != __flag_sta_get(efm_flag1)) {
        time_out++;
        if (time_out >= __g_efm_pgm_timeout) {
            ret = -ETIME;
            break;
        }
    }
    /* 恢复缓存*/
    MODIFY_REG32(HC32F4A0_EFM->FRMC, EFM_CACHE_MASK, tmp);
    return ret;
}

#if 0
/**
 * \brief EFM 读函数
 */
int efm_read(uint32_t addr, uint8_t *p_data, uint32_t data_len){
    uint32_t  data_tmp;
    uint32_t  i;
    uint32_t *p_buf_tmp;
    uint32_t  read_addr;

    if ((p_data == NULL) || (data_len == 0)) {
        return -EINVAL;
    }
    p_buf_tmp = (uint32_t *)p_data;
    read_addr = addr;

    for (i = 0; i < (data_len / 4); i++) {
        p_buf_tmp[i] = RW_MEM32(read_addr);
        read_addr += 4;
    }

    if ((data_len % 4) != 0) {
        data_tmp = RW_MEM32(read_addr);
        for (i = 0; i < (data_len % 4); i++) {
            p_data[(data_len & 0xFFFFFFFC)  + i] = ((data_tmp >> (8 * i)) & 0x000000FF);
        }
    }

    return 0;
}

/**
 * \brief EFM 写函数
 */
int efm_write(uint32_t addr, uint8_t *p_data, uint32_t data_len){
    int       ret          = 0;
    int       sec_num      = -1;
    uint32_t  len_tmp, i, pos;
    uint32_t  sec_offset;
    uint32_t  offset_align = 0;
    uint32_t  data_align   = 0;
    uint32_t *p_sec_buf_tmp;
    uint32_t  sec_addr_start;
    uint32_t  erase_addr, addr_w, len_w;
    uint32_t  data_offset  = 0;
    uint32_t  data_len_tmp = data_len;
    uint32_t  addr_tmp     = addr;

    if ((p_data == NULL) || (data_len == 0)) {
        return -EINVAL;
    }
    /* 要写的数据超过一个扇区了，说明要跨扇区了*/
    if (data_len > SECTOR_SIZE) {
        len_tmp = SECTOR_SIZE;
    } else {
        len_tmp = data_len;
    }

    p_sec_buf_tmp = (uint32_t *)__g_p_sec_buf;
    /* 计算扇区中的偏移*/
    sec_offset = addr % SECTOR_SIZE;

    /* 把地址转换为扇区号*/
    sec_num = efm_sec_num_get(addr);

    /* 这个扇区剩余的空间写不了全部数据，说明要跨扇区*/
    if ((sec_offset + data_len) > SECTOR_SIZE) {
        len_tmp = SECTOR_SIZE - sec_offset;
    }

    /* 检查偏移是否4字节对齐，不对齐则向前对齐*/
    if ((sec_offset % 4) != 0) {
        offset_align = (sec_offset % 4);
    }

    /* EFM 访问写使能*/
    efm_unlock();
    /* EFM 擦写模式寄存器写使能*/
    efm_fwmc_unlock();
    while (len_tmp) {
        /* 扇区写使能*/
        efm_sec_single_unlock(sec_num);

        if (((offset_align + len_tmp) % 4) != 0) {
            data_align = (4 - (offset_align + len_tmp % 4));
        }
        /* 获取扇区起始地址*/
        sec_addr_start = sec_num * SECTOR_SIZE;
        erase_addr     = sec_addr_start;

        /* 获取整个扇区的数据*/
        for(i = 0; i < (SECTOR_SIZE / 4); i++){
            p_sec_buf_tmp[i] = RW_MEM32(erase_addr);
            erase_addr += 4;
        }
        /* 检查扇区是否需要擦除*/
        for(i = 0; i < ((len_tmp + offset_align + data_align) / 4); i++){
            if (p_sec_buf_tmp[((sec_offset - offset_align) / 4) + i] != 0xFFFFFFFF){
                break;
            }
        }
        /* 擦除扇区*/
        if (i < ((len_tmp + offset_align + data_align) / 4)) {
            efm_sec_erase(sec_addr_start);

            addr_w = sec_addr_start;
            len_w  = SECTOR_SIZE;
            pos    = 0;
        } else {
            addr_w = addr_tmp - offset_align;
            len_w  = len_tmp + offset_align + data_align;
            pos    = (sec_offset - offset_align) / 4;
        }
        /* 在原始数据上更新要写的数据*/
        memcpy((__g_p_sec_buf + sec_offset), p_data + data_offset, len_tmp);

        for (i = 0; i < (len_w / 4); i++) {
            ret = efm_single_program(addr_w, p_sec_buf_tmp[pos + i]);
            if (ret != 0) {
                break;
            }
            addr_w += 4;
        }

        efm_sec_single_lock(sec_num);
        if (ret != 0) {
            break;
        }
        data_offset  += len_tmp;
        data_len_tmp -= len_tmp;
        len_tmp = data_len_tmp;
        if (len_tmp > 0) {
            /* 跨扇区*/
            sec_num++;
            sec_offset   = 0;
            offset_align = 0;
            addr_tmp     = 0;
        }
    }
    /* EFM 擦写模式寄存器写保护*/
    efm_fwmc_lock();
    /* EFM 访问写保护*/
    efm_lock();

    return ret;
}
#endif

/**
 * \brief EFM 初始化
 *
 * \retval 成功返回 0
 */
__attribute__((section(".ramfunc"))) int efm_init(void){
    int      flag1, flag2, ret = 0;
    uint32_t tmp;
    uint32_t timeout = 5000;

    /* EFM 访问写使能*/
    efm_unlock();
    /* EFM 擦写模式寄存器写使能*/
    efm_fwmc_unlock();

    /* 关闭指令缓存，数据缓存，预取指功能，数据缓存复位功能和低电压读功能
     * 读插入5个周期*/
    tmp = EFM_WAIT_CYCLE_5 | EFM_INSCACHE_OFF | EFM_DATACACHE_OFF |
          EFM_PREFETCH_OFF | EFM_CACHERST_OFF | EFM_LOWVOLREAD_OFF;

    /* 备份 EFM 寄存器的值，以便反初始化*/
    __g_efm_frmc_bak = READ_REG32(HC32F4A0_EFM->FRMC);
    __g_efm_fwmc_bak = READ_REG32(HC32F4A0_EFM->FWMC);
    __g_efm_fstp_bak = READ_REG32(HC32F4A0_EFM->FSTP);

    /* 配置嵌入式 FLASH*/
    MODIFY_REG32(HC32F4A0_EFM->FRMC, EFM_FRMC_FLWT | EFM_FRMC_PREFE | EFM_FRMC_LVM |
                                     EFM_FRMC_ICACHE | EFM_FRMC_DCACHE | EFM_FRMC_CRST, tmp);
    MODIFY_REG32(HC32F4A0_EFM->FWMC, EFM_FWMC_BUSHLDCTL | EFM_FWMC_PEMOD,
                                     EFM_BUS_BUSY | EFM_MODE_READONLY);
    MODIFY_REG32(HC32F4A0_EFM->FSTP, EFM_FSTP_F0STP | EFM_FSTP_F1STP, EFM_FLASH0_ACT_FLASH1_ACT);

    do {
        flag1 = __flag_sta_get(EFM_FLAG_RDY0);
        flag2 = __flag_sta_get(EFM_FLAG_RDY1);
    } while(((1 != flag1) || (1 != flag2)) && (--timeout));

    if (timeout == 0) {
        /* EFM 擦写模式寄存器写保护*/
        efm_fwmc_lock();
        /* EFM 访问写保护*/
        efm_lock();
        return -ETIME;
    }

    __g_efm_erase_timeout   = sys_clk_hclk_get() / 20;
    __g_efm_pgm_timeout     = sys_clk_hclk_get() / 20000;
    __g_efm_seq_pgm_timeout = sys_clk_hclk_get() / 2000000;

    /* EFM 擦写模式寄存器写保护*/
    efm_fwmc_lock();
    /* EFM 访问写保护*/
    efm_lock();

    return ret;
}
